﻿Imports System.IO
Imports System.Collections
Imports System.Security.Permissions

Public Class FileSplitter
#Region "Static Methods"
    Private Function getMergeFiles(ByVal MergeFileName As String) As String()
        Dim _tempFiles As String() = System.IO.Directory.GetFiles(System.IO.Directory.GetParent(MergeFileName).ToString)
        Dim _MergeFiles() As String = Nothing
        Dim _Index As Short
        Do
            _Index += 1
            If Array.BinarySearch(_tempFiles, MergeFileName & "." & Format(_Index, "000")) >= 0 Then
                ReDim Preserve _MergeFiles(_Index - 1)
                _MergeFiles(_Index - 1) = MergeFileName & "." & Format(_Index, "000")
            Else
                Exit Do
            End If
        Loop
        Return _MergeFiles
    End Function
    <PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _
    Public Function Cut(ByVal src As String, ByVal dest As String, ByVal start As Long, ByVal [end] As Long, ByVal bufsize As Integer, ByRef counter As Integer, Optional ByVal StartProgress As Integer = 0, Optional ByRef LastProgress As Double = 0, Optional ByVal SpliSize As Integer = 0) As Long
        If bufsize > SpliSize Then
            bufsize = SpliSize
        End If
        '   Dim test As Boolean = False
        Dim inbyte As Byte() = New Byte(bufsize - 1) {}
        Dim input As New BufferedStream(File.OpenRead(src), bufsize)
        Dim output As New BufferedStream(File.Create(dest), bufsize)
        If [end] = 0 Then
            [end] = input.Length
        End If
        input.Seek(start, SeekOrigin.Begin)
        counter = 0
        Dim EachProgress As Double
        Dim _Fragments = Math.Floor(FileSize(src) / bufsize)
        EachProgress = 100 / _Fragments
        Dim _FRG = Math.Floor(SpliSize / bufsize)
        Dim CEP As Double = 100 / _FRG
        Dim CAP As Double = 0
        Dim AllProgress As Double = StartProgress
        Dim length As Long
        Try
            While (InlineAssignHelper(bufsize, Input.Read(inbyte, 0, bufsize))) <> 0
                counter = CInt((Input.Position - start) * 100 \ ([end] - start))
                output.Write(inbyte, 0, bufsize)
                '   If test Then
                '   Exit While
                '   End If
                If Input.Position + bufsize > [end] Then
                    bufsize = CInt([end] - Input.Position)
                    '     test = True
                End If
                AllProgress += EachProgress
                CAP += CEP
                RaiseEvent SplitProgressUpdated(Me, New ProgressEventArgs With {.OverallProgress = AllProgress, .CurrentProgress = CAP})
                If bufsize < 0 Then
                    Throw New ArgumentException("The number of parts is too much for this file size.")
                End If
                Forms.Application.DoEvents()
            End While
        Catch ex As Exception
        Finally
            LastProgress = AllProgress
            length = output.Length
            input.Close()
            If output.Length = 0 Then
                output.Close()
                File.Delete(dest)
            Else
                output.Close()
            End If
        End Try
        Return length
    End Function
    Friend Shared Function FileSize(ByVal FileName As String) As Long
        Dim _fileInfo As System.IO.FileInfo
        _fileInfo = New FileInfo(FileName)
        Return _fileInfo.Length
    End Function
    Public Event SplitProgressUpdated As EventHandler(Of ProgressEventArgs)
    Public Event JoinProgressUpdated As EventHandler(Of ProgressEventArgs)
    Public Function Cut(ByVal src As String, ByVal dest As String, ByVal start As Long, ByVal [end] As Long, ByVal bufsize As Integer) As Long
        Dim dummy As Integer
        Return Cut(src, dest, start, [end], bufsize, dummy)
    End Function

    Public Function Cut(ByVal src As String, ByVal dest As String, ByVal start As Long, ByVal [end] As Long) As Long
        Return Cut(src, dest, start, [end], 8192)
    End Function

    Public Shared Function Scan(ByVal src As String, ByVal bufsize As Integer) As Long(,)
        Console.WriteLine("Now scanning. Each block represents " & bufsize & " bytes." & vbLf)
        Dim rangecol As New RangeCollection()
        Dim start As Long = 0
        Dim [end] As Long = 0

        Dim inbyte As Byte() = New Byte(bufsize - 1) {}
        Dim readint As Integer = 0

        Dim input As New BufferedStream(File.OpenRead(src), bufsize)

        While True
            Try
                While (InlineAssignHelper(readint, input.Read(inbyte, 0, bufsize))) <> 0
                    'Console.Write("-");
                    If start <> [end] Then
                        rangecol.Add(New Range(start, input.Position - bufsize))
                    End If
                    start = input.Position
                    [end] = input.Position
                End While
            Catch ex As System.IO.IOException
                Console.Write("X")
                [end] = input.Position + bufsize
                input.Close()
                input = New BufferedStream(File.OpenRead(src), bufsize)
                input.Seek([end], SeekOrigin.Begin)
            End Try
            If readint = 0 Then
                Exit While
            End If
        End While
        If rangecol.Count = 0 Then
            rangecol.Add(New Range(0, 0))
        End If
        Console.WriteLine(vbLf)
        Return rangecol.ToArray()
    End Function

    Public Shared Function Scan(ByVal src As String) As Long(,)
        Return Scan(src, 8192)
    End Function

    Public Sub Split(ByVal src As String, ByVal dest As String, ByVal splitsize As Long, ByVal bufsize As Integer, ByRef counter As Integer)
        Dim count As Integer = 0
        Dim start As Long = 0
        Dim [end] As Long = splitsize
        Dim LP As Double = 0
        'System.Math.Max(System.Threading.Interlocked.Increment(count), count - 1)
        While splitsize = Cut(src, dest & "." & Format(System.Math.Max(System.Threading.Interlocked.Increment(count), count - 1), "000"), start, [end], bufsize, counter, LP, LP, splitsize)
            start = [end]
            [end] += splitsize
        End While
        RaiseEvent SplitProgressUpdated(Me, New ProgressEventArgs With {.OverallProgress = 100})
    End Sub

    Public Sub Split(ByVal src As String, ByVal dest As String, ByVal splitsize As Long, ByVal bufsize As Integer)
        Dim counter As Integer
        Split(src, dest, splitsize, bufsize, counter)
    End Sub

    Public Sub Split(ByVal src As String, ByVal dest As String, ByVal splitsize As Long)
        Dim dummy As Integer
        Split(src, dest, splitsize, 8192, dummy)
    End Sub

    Public Sub Join(ByVal dest As String, ByVal bufsize As Integer, ByRef counter As Integer, ByVal ParamArray src As String())
        counter = 0
        Dim origbufsize As Integer = bufsize
        Dim inbyte As Byte() = New Byte(bufsize - 1) {}
        Try
            File.Create(dest).Close()
            File.Delete(dest)
        Catch ex As Exception
            dest += "Untitled"
        End Try
        Dim output As New BufferedStream(File.Create(dest), bufsize)
        Dim AllSize As Double
        For Each Item In src
            AllSize += FileSize(Item)
        Next
        Dim _Progress As Double = 100 / (Math.Floor(AllSize) / bufsize)
        Dim AllProgress As Double
        For Each srcfile As String In src
            bufsize = origbufsize
            Dim input As New BufferedStream(File.OpenRead(srcfile), bufsize)
            Dim CAP As Double = 0
            While (InlineAssignHelper(bufsize, input.Read(inbyte, 0, bufsize))) <> 0
                Dim _FRG = Math.Floor(FileSize(srcfile) / bufsize)
                Dim CEP As Double = 100 / _FRG
                counter = CInt(input.Position * 100 \ input.Length)
                output.Write(inbyte, 0, bufsize)
                CAP += CEP
                AllProgress += _Progress
                RaiseEvent JoinProgressUpdated(Me, New ProgressEventArgs With {.OverallProgress = AllProgress, .CurrentProgress = CAP})
            End While
            input.Close()
            RaiseEvent JoinProgressUpdated(Me, New ProgressEventArgs With {.OverallProgress = AllProgress, .CurrentProgress = 100})
        Next
        output.Close()
        RaiseEvent JoinProgressUpdated(Me, New ProgressEventArgs With {.OverallProgress = 100, .CurrentProgress = 100})
    End Sub

    Public Sub Join(ByVal dest As String, ByVal bufsize As Integer, ByVal ParamArray src As String())
        Dim dummy As Integer
        Join(dest, bufsize, dummy, src)
    End Sub
    Public Sub Join(ByVal dest As String, ByVal ParamArray src As String())
        Join(dest, 8192, src)
    End Sub
    Public Sub Join(ByVal ParamArray src As String())
        Join(GetPathWithoutExtension(src(0)), 8192, src)
    End Sub
    Public Sub Join(ByVal src As String)
        Dim FullPath As String = GetPathWithoutExtension(src)
        Join(FullPath, 8192, getMergeFiles(FullPath))
    End Sub
    Public Sub Join(ByVal src As String, ByVal dest As String)
        Dim FullPath As String = GetPathWithoutExtension(src)
        Join(dest, 8192, getMergeFiles(FullPath))
    End Sub
    Public Shared Function GetPathWithoutExtension(ByVal FilePath As String) As String
        Dim _fileInfo As System.IO.FileInfo
        _fileInfo = New FileInfo(FilePath)
        Return _fileInfo.FullName.Substring(0, _fileInfo.FullName.Length - _fileInfo.Extension.Length)
    End Function
    Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
        target = value
        Return value
    End Function
#End Region
End Class

Friend Class Range
    Private m_start As Long
    Private m_end As Long

    Friend ReadOnly Property Start() As Long
        Get
            Return m_start
        End Get
    End Property
    Friend ReadOnly Property [End]() As Long
        Get
            Return m_end
        End Get
    End Property

    Friend Sub New(ByVal start As Long, ByVal [end] As Long)
        Me.m_start = start
        Me.m_end = [end]
    End Sub
End Class

Friend Class RangeCollection
    Inherits ArrayList
    Default Friend Overloads Property Item(ByVal index As Integer) As Range
        Get
            Return TryCast(MyBase.Item(index), Range)
        End Get
        Set(ByVal value As Range)
            MyBase.Item(index) = TryCast(value, Range)
        End Set
    End Property

    Friend Overloads Function ToArray() As Long(,)
        Dim output As Long(,) = New Long(Count - 1, 1) {}
        Dim i As Integer = 0
        For Each range As Range In Me
            output(i, 0) = range.Start
            output(System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1), 1) = range.[End]
        Next
        Return output
    End Function
End Class